#include "Vector4.h"
#include "Vector3.h"

#include <Rz/Math.h>

namespace Rz { namespace Graphics { namespace Math {

using namespace Rz::Math;

// TODO: RZ_ASSERT
Vector4::Vector4(std::initializer_list<f32> v)
	: X(*v.begin())
	, Y(*(v.begin() + 1))
	, Z(*(v.begin() + 2))
	, W(*(v.begin() + 3))
{

}

Vector4::Vector4():
	X(0.0f),
	Y(0.0f),
	Z(0.0f),
	W(0.0f)
{

}

Vector4::Vector4(f32 x, f32 y, f32 z, f32 w):
	X(x),
	Y(y),
	Z(z),
	W(w)
{

}

Vector4::Vector4(const f32* ptr):
	X(ptr[0]),
	Y(ptr[1]),
	Z(ptr[2]),
	W(ptr[3])
{

}

Vector4::Vector4(const Vector3& vec):
	X(vec.X),
	Y(vec.Y),
	Z(vec.Z),
	W(1.0f)
{

}

Vector4::Vector4(const Vector4& vec):
	X(vec.X),
	Y(vec.Y),
	Z(vec.Z),
	W(vec.W)
{

}

void Vector4::Set(f32 x, f32 y, f32 z, f32 w)
{
	X = x;
	Y = y;
	Z = z;
	W = w;
}

f32 Vector4::Length() const
{
	return Math::Sqrt(X * X + Y * Y + Z * Z + W * W);
}

void Vector4::Normalize()
{
	f32 len = Length();

	if (len)
	{
		len = 1.0f / len;
		X *= len;
		Y *= len;
		Z *= len;
		W *= len;
	}
}

Vector4 Vector4::Normalized() const
{
	Vector4 result = (*this);
	result.Normalize();
	return result;
}

f32 Vector4::Distance(const Vector4& u, const Vector4& v)
{
	return Math::Sqrt((u.X - v.X) * (u.X - v.X) + (u.Y - v.Y) * (u.Y - v.Y) + (u.Z - v.Z) * (u.Z - v.Z) + (u.W - v.W) * (u.W - v.W));
}

f32 Vector4::Dot(const Vector4& u, const Vector4& v)
{
	return u.X * v.X + u.Y * v.Y + u.Z * v.Z + u.W * v.W;
}


f32 Vector4::operator[] (s32 index) const
{
	return Cell[index];
}

f32& Vector4::operator[] (s32 index)
{
	return Cell[index];
}

Vector4 Vector4::operator + (const Vector4& vec) const
{
	Vector4 result = (*this);
	result.X += vec.X;
	result.Y += vec.Y;
	result.Z += vec.Z;
	result.W += vec.W;
	return result;
}

Vector4 Vector4::operator - (const Vector4& vec) const
{
	Vector4 result = (*this);
	result.X -= vec.X;
	result.Y -= vec.Y;
	result.Z -= vec.Z;
	result.W -= vec.W;
	return result;
}

Vector4 Vector4::operator * (const Vector4& vec) const
{
	Vector4 result = (*this);
	result.X *= vec.X;
	result.Y *= vec.Y;
	result.Z *= vec.Z;
	result.W *= vec.W;
	return result;
}

Vector4 Vector4::operator / (const Vector4& vec) const
{
	Vector4 result = (*this);
	result.X /= vec.X;
	result.Y /= vec.Y;
	result.Z /= vec.Z;
	result.W /= vec.W;
	return result;
}

Vector4 Vector4::operator + (f32 v) const
{
	Vector4 result = (*this);
	result.X += v;
	result.Y += v;
	result.Z += v;
	result.W += v;
	return result;
}

Vector4 Vector4::operator - (f32 v) const
{
	Vector4 result = (*this);
	result.X -= v;
	result.Y -= v;
	result.Z -= v;
	result.W -= v;
	return result;
}

Vector4 Vector4::operator * (f32 v) const
{
	Vector4 result = (*this);
	result.X *= v;
	result.Y *= v;
	result.Z *= v;
	result.W *= v;
	return result;
}

Vector4 Vector4::operator / (f32 v) const
{
	Vector4 result = (*this);
	result.X /= v;
	result.Y /= v;
	result.Z /= v;
	result.W /= v;
	return result;
}

Vector4& Vector4::operator += (const Vector4& v)
{
	*this = *this + v;
	return *this;
}

Vector4& Vector4::operator -= (const Vector4& v)
{
	*this = *this - v;
	return *this;
}

Vector4& Vector4::operator *= (const Vector4& v)
{
	*this = *this * v;
	return *this;
}

Vector4& Vector4::operator /= (const Vector4& v)
{
	*this = *this / v;
	return *this;
}

Vector4& Vector4::operator += (f32 v)
{
	*this = *this + v;
	return *this;
}

Vector4& Vector4::operator -= (f32 v)
{
	*this = *this - v;
	return *this;
}

Vector4& Vector4::operator *= (f32 v)
{
	*this = *this * v;
	return *this;
}

Vector4& Vector4::operator /= (f32 v)
{
	*this = *this / v;
	return *this;
}

Vector4 Vector4::operator - () const
{
	Vector4 result = *this;

	result.X = -result.X;
	result.Y = -result.Y;
	result.Z = -result.Z;
	result.W = -result.W;

	return result;
}

Vector4 operator + (f32 v, const Vector4& u) 
{
	return u + v;
}

Vector4 operator - (f32 v, const Vector4& u)
{
	return u - v;
}

Vector4 operator * (f32 v, const Vector4& u)
{
	return u * v;
}

Vector4 operator / (f32 v, const Vector4& u)
{
	return u / v;
}

//////////////////////////////////////////////////////////////////////////
// IHumanReadable
String Vector4::ToString() const
{
	return String::Format("(%.3f, %.3f, %.3f, %.3f)", X, Y, Z, W);
}

//////////////////////////////////////////////////////////////////////////
// IEquatable
bool Vector4::Equals(const Vector4& r) const
{
	return X == r.X && Y == r.Y && Z == r.Z && W == r.W;
}

bool Vector4::operator == (const Vector4& r) const
{
	return  Equals(r);
}

bool Vector4::operator != (const Vector4& r) const
{
	return !Equals(r);
}

//////////////////////////////////////////////////////////////////////////
// consts

const Vector4 Vector4::Zero    = Vector4(0.0f, 0.0f, 0.0f, 0.0f);
const Vector4 Vector4::One     = Vector4(1.0f, 1.0f, 1.0f, 1.0f);
const Vector4 Vector4::NormalX = Vector4(1.0f, 0.0f, 0.0f, 0.0f);
const Vector4 Vector4::NormalY = Vector4(0.0f, 1.0f, 0.0f, 0.0f);
const Vector4 Vector4::NormalZ = Vector4(0.0f, 0.0f, 1.0f, 0.0f);
const Vector4 Vector4::NormalW = Vector4(0.0f, 0.0f, 0.0f, 1.0f);

} } }
